package com.ark.cloud.dataservices.auth.service.impl;

import com.ark.cloud.dataservices.auth.service.IAuthenticationService;
import com.ark.cloud.dataservices.common.exception.AuthErrorType;
import com.ark.cloud.dataservices.modules.sys.entity.po.Resource;
import com.ark.cloud.dataservices.modules.sys.service.IResourceService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * Copyright(c): 2021 Cloud-Ark Inc.
 * Author(s): Ma Wei (mawei@cloud-ark.com)
 */
@Service
@Slf4j
public class AuthenticationService implements IAuthenticationService {

    private final HandlerMappingIntrospector mvcHandlerMappingIntrospector;

    private final IResourceService resourceService;

    private final AntPathMatcher antPathMatcher;

    public AuthenticationService(HandlerMappingIntrospector mvcHandlerMappingIntrospector, IResourceService resourceService, AntPathMatcher antPathMatcher) {
        this.mvcHandlerMappingIntrospector = mvcHandlerMappingIntrospector;
        this.resourceService = resourceService;
        this.antPathMatcher = antPathMatcher;
    }

    /**
     * 有权限通过, 无权限或全局资源中未找到请求 url 返回否
     *
     * @param url,method 请求 url 与方法
     * @param request    {@link HttpServletRequest}
     * @param response   {@link HttpServletResponse}
     */
    @Override
    public void authenticate(HttpServletRequest request, HttpServletResponse response, String authentication, String url, String method) throws ServletException, IOException {
        log.debug("正在访问的url是:{}，method:{}", url, method);
        //获取当前访问用户认证信息
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        //获取此访问用户所有角色拥有的权限资源
        List<Resource> resources = resourceService.query(auth.getName());
        if (CollectionUtils.isEmpty(resources)) {
            log.error("当前用户没有被授予资源信息");
            request.setAttribute("permissionDeniedException", AuthErrorType.INSUFFICIENT_SCOPE.getMesg());
            request.getRequestDispatcher("/permissionDeniedException").forward(request, response);
        }
        //用户拥有权限资源 与 url要求的资源进行对比
        if (log.isDebugEnabled()) {
            log.debug("用户被授予角色的资源数量是:{}, 资源集合信息为:{}", resources.size(), resources);
        }
        if (!isMatch(url, resources)) {
            log.error("url未在资源池中找到，拒绝访问");
            request.setAttribute("permissionDeniedException", AuthErrorType.INSUFFICIENT_SCOPE.getMesg());
            request.getRequestDispatcher("/permissionDeniedException").forward(request, response);
        }
    }

    /**
     * url对应资源与用户拥有资源进行匹配
     *
     * @param urlAttribute  请求 url
     * @param userResources 资源列表
     * @return 是否匹配
     */
    public boolean isMatch(String urlAttribute, List<Resource> userResources) {
        for (Resource resource : userResources) {
            if (antPathMatcher.match(resource.getUrl(), urlAttribute)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 创建RequestMatcher
     *
     * @param url    请求 url
     * @param method 请求方法
     * @return {@link MvcRequestMatcher}
     */
    private MvcRequestMatcher newMvcRequestMatcher(String url, String method) {
        return new NewMvcRequestMatcher(mvcHandlerMappingIntrospector, url, method);
    }
}